package com.neidetcher.toolkit.util;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import javax.xml.namespace.NamespaceContext;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathFactory;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.w3c.dom.Document;

/**
 * This is supposed to make it very easy to deal with XPath.  The existing XPath APIs
 * are insane.
 */
public class Xml
{
   private Log log = LogFactory.getLog(Xml.class);
   
   private String string = null;

   private Map<String, String> nsMap = null;
   
   private Xml(){} // no default constructor, gotta give us a String
   
   public Xml(String stringIn)
   {
      log.debug("setting string to: " + stringIn);
      if (stringIn == null || "".equals(stringIn))
      {
         throw new IllegalArgumentException("string can't be null or blank");
      }
      string = stringIn;
   }
   
   public void addUriPrefixMapping(String prefix, String uri)
   {
      if (nsMap == null)
      {
         nsMap = new HashMap<String, String>();
      }
      nsMap.put(prefix, uri);
   }
   
   /**
    * given an xpath expression, we'll execute it on our xml.
    */
   public String getByXpath(String xpathExpression)
   {
      log.debug("getByXpath(" + xpathExpression + ")");
      String result = null;
      
      DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
      domFactory.setNamespaceAware(true); // never forget this!

      try
      {
         DocumentBuilder builder = domFactory.newDocumentBuilder();
         Document doc = builder.parse(Io.getInputStreamFromString(string));
         
         XPathFactory factory = XPathFactory.newInstance();
         XPath xpath = factory.newXPath();
         if (nsMap != null)
         {
            xpath.setNamespaceContext(new MyNamespaceContext(nsMap));
         }
         
         result = xpath.evaluate(xpathExpression, doc);
         
         log.debug("result: " + result);
      } 
      catch (Exception e)
      {
         // something bad happened
         e.printStackTrace();
      } 

      return result;
   }
   
   public String getString()
   {
      return string;
   }
   
   // this is just used for sending namespace info to the parser
   private class MyNamespaceContext implements NamespaceContext
   {
      Map<String, String> map = new HashMap<String, String>();
      
      public MyNamespaceContext(Map<String, String> mapIn)
      {
         map = mapIn;
      }

      public String getNamespaceURI(String prefix)
      {
         return (String) map.get(prefix);
         // TODO Auto-generated method stub
         //return null;
      }

      public String getPrefix(String namespaceURI)
      {
         // TODO Auto-generated method stub
         for(String currKey : map.keySet())
         {
            if (map.get(currKey).equals(namespaceURI))
            {
               return currKey;
            }
         }
         return null;
      }

      public Iterator getPrefixes(String namespaceURI)
      {
         // TODO Auto-generated method stub
         return null;
      }
   }  
}
